package com.cg.utl;

import java.sql.*;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * JDBC工具类（使用Druid数据库连接池）
 *
 */
public class JDBCUtil {

    private static final String URL = "jdbc:mysql://localhost:3306/mvc";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "452369";
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 获取工具类实例
    public static JDBCUtil getInstance() {
        return new JDBCUtil();
    }

    // 数据库连接
    private Connection connection;
    // statement对象，执行SQL
    private PreparedStatement preparedStatement;
    // 查询结果集
    private ResultSet resultSet;

    /**
     * 从数据库连接池中获取连接，因为是内部使用，所以设为private不暴露给外界使用
     *
     * @return 从数据库连接池中获取连接
     */
    private Connection getConnection() {
        connection = null;
        try {
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 释放数据库资源
     */
    public void close() {
        // 释放数据库资源
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行增删改sql语句
     *
     * @param sql
     *            SQL语句，其中使用?占位符表示参数
     * @param params
     *            SQL中需要的参数值，需要按照SQL中?的顺序，将参数值放入List中
     * @return 是否执行成功
     */
    public boolean update(String sql, List<Object> params) {

        // 获取连接
        getConnection();
        int rows = 0;
        if (connection != null) {
            try {
                // 获取PreparedStatement对象
                preparedStatement = connection.prepareStatement(sql);
                // 循环设置参数
                for (int i = 0; i < params.size(); i++) {
                    preparedStatement.setObject(i + 1, params.get(i));
                }
                // 这一步非常必要，帮助我们查看执行的sql到底是什么样！用于解决问题
                guessPreparedSql(sql, params);
                // 执行SQL语句
                rows = preparedStatement.executeUpdate();

            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return rows > 0;
    }

    /**
     * 执行select语句
     *
     * @param sql
     *            SQL语句，其中使用?占位符表示参数
     * @param params
     *            SQL中需要的参数值，需要按照SQL中?的顺序，将参数值放入List中
     * @return 查询结果集
     */
    public ResultSet query(String sql, List<Object> params) {
        // 获取连接
        getConnection();
        if (connection != null) {
            try {
                // 获取PreparedStatement对象
                preparedStatement = connection.prepareStatement(sql);
                // 循环设置参数
                for (int i = 0; i < params.size(); i++) {
                    preparedStatement.setObject(i + 1, params.get(i));
                }
                // 这一步非常必要，帮助我们查看执行的sql到底是什么样！
                guessPreparedSql(sql, params);
                // 执行sql，并获取结果集
                resultSet = preparedStatement.executeQuery();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // 需要返回ResultSet需要提供给外部使用
        return resultSet;
    }

    /**
     * 执行无参select语句
     *
     * @param sql
     *            SQL语句
     * @return 结果集
     */
    public ResultSet query(String sql) {
        return query(sql, Collections.emptyList());
    }

    /**
     * 获得PreparedStatement向数据库提交的SQL语句
     *
     * @param sql
     * @param params
     * @return
     */
    private String guessPreparedSql(String sql, List<Object> params) {
        int paramNum = 0;

        // 如果参数集不为空，取得其长度
        if (null != params) {
            paramNum = params.size();
        }

        // 如果没有参数，说明不是动态SQL语句，直接返回原sql
        if (1 > paramNum) {
            System.out.println(LocalDateTime.now() + "\tprepared sql: " + sql);
            return sql;
        }

        // 如果有参数，则是动态SQL语句，需要构造并返回新sql
        StringBuffer returnSQL = new StringBuffer();
        String[] subSQL = sql.split("\\?");

        // 开始循环替换问号为参数值
        for (int i = 0; i < paramNum; i++) {
            Object value = params.get(i);
            if (value == null) {
                System.err.printf("第 %d 个参数的值为 null %n", i + 1);
                returnSQL.append(subSQL[i]).append(value);
                continue;
            }
            if (value instanceof Number) {
                // 数值不需要引号
                returnSQL.append(subSQL[i]).append(value);
            } else {
                // 非数值需要引号，遇到特殊字符将其转义输出
                String str = value.toString().replaceAll("(['\\\\])", "\\\\$1");
                returnSQL.append(subSQL[i]).append(" '").append(str).append("' ");
            }
        }

        // 如果问号不是原sql的最后一个字符，则将改问号后的部分添加到returnSQL中
        if (subSQL.length > params.size()) {
            returnSQL.append(subSQL[subSQL.length - 1]);
        }

        String formatSql = returnSQL.toString();
        System.out.println(LocalDateTime.now() + "\tprepared sql: " + formatSql);

        return formatSql;
    }
}
